package com.example.rtsp2.service.impl;

import com.example.rtsp2.service.FFmpegService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;

public class FFmpegServiceImpl implements FFmpegService {
    private static final Logger logger = LoggerFactory.getLogger(FFmpegServiceImpl.class);
    private static final int MAX_BUFFER_SIZE = 1024 * 1024; // 1MB

    private final String rtspUrl;
    private final int fps;
    private final int width;
    private final int height;

    private Consumer<byte[]> frameConsumer;
    private final AtomicBoolean running = new AtomicBoolean(false);
    private final AtomicBoolean paused = new AtomicBoolean(false);
    private Process ffmpegProcess;
    private ExecutorService executorService;
    private Runnable stopCallback;

    // 重试计数
    private final AtomicInteger retryCount = new AtomicInteger(0);
    private static final int MAX_RETRIES = 5;

    public FFmpegServiceImpl(String rtspUrl) {
        this(rtspUrl, 15, 1280, 720);
    }

    public FFmpegServiceImpl(String rtspUrl, int fps, int width, int height) {
        this.rtspUrl = rtspUrl;
        this.fps = fps;
        this.width = width;
        this.height = height;
    }

    // 设置帧消费者
    @Override
    public void setFrameConsumer(Consumer<byte[]> frameConsumer) {
        this.frameConsumer = frameConsumer;
    }

    @Override
    public void start() {
        if (running.get()) {
            return;
        }

        running.set(true);
        paused.set(false);

        if (executorService != null && !executorService.isShutdown()) {
            executorService.shutdown();
        }

        executorService = Executors.newSingleThreadExecutor();
        executorService.execute(this::captureFrames);

        logger.info("FFmpeg服务已启动，RTSP源: {}", rtspUrl);
    }

    @Override
    public void stop() {
        running.set(false);
        paused.set(false);

        if (ffmpegProcess != null) {
            ffmpegProcess.destroy();
            try {
                ffmpegProcess.waitFor();
            } catch (InterruptedException e) {
                logger.warn("等待FFmpeg进程终止时被中断", e);
                Thread.currentThread().interrupt();
            }
            ffmpegProcess = null;
        }

        if (executorService != null) {
            executorService.shutdown();
            executorService = null;
        }

        logger.info("FFmpeg服务已停止");
    }

    @Override
    public void pause() {
        try {
            if (running.get() && !paused.get()) {
                paused.set(true);
                logger.info("FFmpeg服务已暂停，RTSP源: {}", rtspUrl);
            } else {
                logger.warn("无法暂停FFmpeg服务，运行状态: {}, 暂停状态: {}", running.get(), paused.get());
            }
        } catch (Exception e) {
            logger.error("暂停FFmpeg服务时发生异常", e);
            throw new RuntimeException("暂停FFmpeg服务失败", e);
        }
    }

    @Override
    public void resume() {
        try {
            if (running.get() && paused.get()) {
                paused.set(false);
                logger.info("FFmpeg服务已恢复，RTSP源: {}", rtspUrl);
            } else {
                logger.warn("无法恢复FFmpeg服务，运行状态: {}, 暂停状态: {}", running.get(), paused.get());
            }
        } catch (Exception e) {
            logger.error("恢复FFmpeg服务时发生异常", e);
            throw new RuntimeException("恢复FFmpeg服务失败", e);
        }
    }

    @Override
    public boolean isRunning() {
        return running.get();
    }

    @Override
    public boolean isPaused() {
        return paused.get();
    }

    @Override
    public String getRtspUrl() {
        return rtspUrl;
    }

    /**
     * 启动FFmpeg进程，并捕获视频帧
     */
    private void captureFrames() {
        try {
            String[] cmd = buildFFmpegCommand();
            logger.info("启动FFmpeg命令: {}", String.join(" ", cmd));

            // 创建ProcessBuilder并设置错误流重定向
            ProcessBuilder processBuilder = new ProcessBuilder(cmd);
            processBuilder.redirectErrorStream(true);
            ffmpegProcess = processBuilder.start();
            // 重置重试计数
//            retryCount.set(0);
            // 启动线程读取输入流
            readBinaryStream(ffmpegProcess.getInputStream());

            int exitCode = ffmpegProcess.waitFor();
            logger.info("FFmpeg进程退出，代码: {}", exitCode);

        } catch (IOException | InterruptedException e) {
            logger.error("FFmpeg处理异常", e);
            if (e instanceof InterruptedException) {
                Thread.currentThread().interrupt();
            }
        } finally {
            // 修复：只检查运行状态，不检查streamSessions
            if (running.get()) {
                int currentRetry = retryCount.incrementAndGet();
                if (currentRetry <= MAX_RETRIES) {
                    logger.warn("FFmpeg进程异常退出，尝试重启... ({}/{})", currentRetry, MAX_RETRIES);
                    try {
                        long waitTime = Math.min(5000 * (long) Math.pow(2, currentRetry - 1), 60000);
                        Thread.sleep(waitTime);
                    } catch (InterruptedException e) {
                        logger.warn("重试等待被中断", e);
                        Thread.currentThread().interrupt();
                        return;
                    }
                    captureFrames();
                } else {
                    logger.error("达到最大重试次数，停止尝试重启FFmpeg");
                    running.set(false);
                    // 发送错误消息到所有会话
                    try {
                        byte[] errorMessage = "ERROR:FFmpeg服务已停止，无法继续传输视频流".getBytes(StandardCharsets.UTF_8);
                        frameConsumer.accept(errorMessage);
                    }catch (Exception e) {
                        logger.error("发送错误消息失败", e);
                    }
                }
            } else {
                logger.info("FFmpeg服务已停止，不进行重试");
                running.set(false);
            }
        }
    }

    /**
     * 读取二进制数据流，并调用帧消费者处理视频帧
     */
    private void readBinaryStream(InputStream inputStream) {
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        byte[] chunk = new byte[8192];
        int bytesRead;

        try {
            while (running.get() && (bytesRead = inputStream.read(chunk)) != -1) {
                if (paused.get()) {
                    continue;
                }

                buffer.write(chunk, 0, bytesRead);

                if (buffer.size() > MAX_BUFFER_SIZE) {
                    buffer.reset();
                    logger.warn("缓冲区过大，已重置");
                }

                byte[] data = buffer.toByteArray();

                int start = findJpegStart(data);
                while (start >= 0) {
                    int end = findJpegEnd(data, start);
                    if (end > start) {
                        byte[] frame = new byte[end - start + 2];
                        System.arraycopy(data, start, frame, 0, frame.length);

                        if (frameConsumer != null) {
                            frameConsumer.accept(frame);
                        }

                        byte[] remaining = new byte[data.length - end - 2];
                        System.arraycopy(data, end + 2, remaining, 0, remaining.length);
                        data = remaining;
                        start = findJpegStart(data);
                    } else {
                        break;
                    }
                }

                buffer.reset();
                if (data.length > 0) {
                    buffer.write(data);
                }

                Thread.yield();
            }
        } catch (IOException e) {
            logger.error("读取FFmpeg输出流失败", e);
        }
    }

    /**
     * 查找JPEG图片的起始位置
     */
    private int findJpegStart(byte[] data) {
        for (int i = 0; i < data.length - 1; i++) {
            if (data[i] == (byte)0xFF && data[i+1] == (byte)0xD8) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 查找JPEG图片的结束位置
     */
    private int findJpegEnd(byte[] data, int start) {
        for (int i = start + 2; i < data.length - 1; i++) {
            if (data[i] == (byte)0xFF && data[i+1] == (byte)0xD9) {
                return i;
            }
        }
        return -1;
    }

    @Override
    public void setStopCallback(Runnable stopCallback) {
        this.stopCallback = stopCallback;
    }


    /**
     * 构建FFmpeg命令行参数
     */
    private String[] buildFFmpegCommand() {
        return new String[] {
                "ffmpeg",
                "-rtsp_transport", "tcp",
                "-fflags", "nobuffer",
                "-flags", "low_delay",
                "-avioflags", "direct",
                "-analyzeduration", "100000",
                "-probesize", "8192",
                "-i", rtspUrl,
                "-f", "image2",
                "-c:v", "mjpeg",
                "-q:v", "6",
                "-vf", String.format("fps=%d,scale=%d:%d", fps, width, height),
                "-vsync", "0",
                "-avioflags", "direct",
                "-threads", "2",
                "-tune", "zerolatency",
                "-preset", "ultrafast",
                "-update", "1",
                "pipe:1"
        };
    }
}